package redisCluster

import (
	"reflect"
	"strconv"
	"time"

	"github.com/go-redis/redis"
	"github.com/pquerna/ffjson/ffjson"
)

// HmDelete 删除hash数据
func HmDelete(key string, fields []string) (err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.HDel(key, fields...)

	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionHmDelete, err, "key: %v, fields: %v", key, fields)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionHmDelete, err, "key: %v, fields: %v", key, fields)
		return
	}

	return nil
}

// HMDEL 删除hash数据
func HDEL(key string, fields []interface{}) (err error) {
	var fieldsStr []string
	for _, field := range fields {
		fieldsStr = append(fieldsStr, field.(string))
	}
	err = HmDelete(key, fieldsStr)
	errPrintf(ActionHDEL, err, "key: %v, fields: %v", key, fields)
	return
}

// HMGET .
func HMGET(key string, fields []interface{}, t interface{}) (ret []interface{}, err error) {
	var (
		values []interface{}
		out    = make([]interface{}, 0, 2)
	)
	client := GetDefaultRedisClusterClient()

	if len(fields) > 0 {
		var fieldsStr []string
		for _, field := range fields {
			fieldsStr = append(fieldsStr, field.(string))
		}
		cmd := client.HMGet(key, fieldsStr...)
		if cmd == nil {
			out, err = nil, ErrorUnknown
			errPrintf(ActionHMGET, err, "key: %v, fields: %v", key, fields)
			return
		}
		if cmd.Err() != nil {
			out, err = nil, cmd.Err()
			errPrintf(ActionHMGET, err, "key: %v, fields: %v", key, fields)
			return
		}
		values = cmd.Val()
	} else {
		cmd := client.HGetAll(key)
		if cmd == nil {
			ret, err = nil, ErrorUnknown
			errPrintf(ActionHMGET, err, "key: %v, fields: %v", key, fields)
			return
		}
		if cmd.Err() != nil {
			ret, err = nil, cmd.Err()
			errPrintf(ActionHMGET, err, "key: %v, fields: %v", key, fields)
			return
		}

		values = []interface{}{}
		for _, v := range cmd.Val() {
			values = append(values, v)
		}
	}

	for _, v := range values {
		if v == nil {
			continue
		}

		var s interface{}
		itype := reflect.ValueOf(t).Kind()
		switch itype {
		case reflect.Struct:
			if len(v.(string)) > 0 {
				s = reflect.New(reflect.TypeOf(t)).Interface()
				if err = ffjson.Unmarshal([]byte(v.(string)), s); err != nil {
					errPrintf(ActionHMGET, err, "key: %v, fields: %v", key, fields)
					return nil, err
				}
			}
		case reflect.String, reflect.Slice:
			s = v
		}

		out = append(out, s)
	}
	return out, nil
}

// HMSET .
func HMSET(key string, fieldsValues map[string]interface{}) (err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.HMSet(key, fieldsValues)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionHMSET, err, "key: %v, fieldsValues: %v", key, fieldsValues)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionHMSET, err, "key: %v, fieldsValues: %v", key, fieldsValues)
		return
	}

	return nil
}

// Exists key是否存在
func Exists(key string) (ret bool, err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.Exists(key)
	if cmd == nil {
		ret, err = false, ErrorUnknown
		errPrintf(ActionExists, err, "key: %v", key)
		return
	}
	if cmd.Err() != nil {
		ret, err = false, cmd.Err()
		errPrintf(ActionExists, err, "key: %v", key)
		return
	}

	return cmd.Val() == ResultYes, nil
}

// Delete 删除key
func Delete(key string) (err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.Del(key)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionDelete, err, "key: %v", key)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionDelete, err, "key: %v", key)
		return
	}

	return nil
}

// Incr 自增
func Incr(key string) (err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.Incr(key)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionIncr, err, "key: %v", key)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionExpire, err, "key: %v", key)
		return
	}

	return nil
}

// Expire 设置key多少秒后超时
func Expire(key string, seconds int) (err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.Expire(key, time.Second*time.Duration(seconds))
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionExpire, err, "key: %v, seconds: %v", key, seconds)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionExpire, err, "key: %v, seconds: %v", key, seconds)
		return
	}

	return nil
}

// SetObjectWithExpire 保存key的对象且有超时
func SetObjectWithExpire(key string, value interface{}, expire int) (err error) {
	var fieldsValues map[string]interface{}
	if fieldsValues, err = flattenStruct(value); err == nil {
		err = HMSET(key, fieldsValues)
	}
	if err == nil && expire > 0 {
		err = Expire(key, expire)
	}
	errPrintf(ActionSetObjectWithExpire, err, "key: %v, value: %v, expire: %v", key, value, expire)
	return err
}

// GetObject 按key读取对象
func GetObject(key string, value interface{}) (err error) {
	client := GetDefaultRedisClusterClient()

	cmd := client.HGetAll(key)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionGetObject, err, "key: %v, value: %v", key, value)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionGetObject, err, "key: %v, value: %v", key, value)
		return
	}

	err = fillStruct(cmd.Val(), value)
	errPrintf(ActionGetObject, nil, "key: %v, value: %v", key, value)
	return
}

// Hincrby .
func Hincrby(key string, fieldsValues map[string]int, expire int) (err error) {
	client := GetDefaultRedisClusterClient()

	pipe := client.TxPipeline()
	for field, value := range fieldsValues {
		cmd := pipe.HIncrBy(key, field, int64(value))
		if cmd == nil {
			err = ErrorUnknown
			break
		} else if cmd.Err() != nil {
			err = cmd.Err()
			break
		}
	}
	if err == nil && expire > 0 {
		cmd := pipe.Expire(key, time.Duration(expire)*time.Second)
		if cmd == nil {
			err = ErrorUnknown
		} else if cmd.Err() != nil {
			err = cmd.Err()
		}
	}
	if err == nil {
		_, err = pipe.Exec()
	}
	if err != nil {
		errPrintf(ActionHincrby, err, "key: %v, fieldsValues: %v", key, fieldsValues)
	} else {
		errPrintf(ActionHincrby, nil, "key: %v, fieldsValues: %v", key, fieldsValues)
	}
	return
}

// HmgetKeysValues .
func HmgetKeysValues(keys []string, fields []string, in interface{}) (err error) {
	var values reflect.Value
	client := GetDefaultRedisClusterClient()

	typeOfIn := reflect.TypeOf(in)
	if typeOfIn.Kind() != reflect.Ptr || typeOfIn.Elem().Kind() != reflect.Slice {
		return ErrInputNotSlicePtr
	}
	typeOfBase := typeOfIn.Elem().Elem()
	values = reflect.ValueOf(in).Elem()

	for _, key := range keys {
		cmd := client.HMGet(key, fields...)
		if cmd == nil {
			err = ErrorUnknown
			break
		} else if cmd.Err() != nil {
			err = cmd.Err()
			break
		}
		vals := cmd.Val()
		for _, v := range vals {
			if v == nil {
				newValue := reflect.New(typeOfBase)
				values = reflect.Append(values, newValue.Elem())
				continue
			}
			switch typeOfBase.Kind() {
			case reflect.Struct:
				newValue := reflect.New(typeOfBase)
				if err = ffjson.Unmarshal([]byte(v.(string)), newValue.Interface()); err != nil {
					break
				}
				values = reflect.Append(values, newValue.Elem())
			case reflect.String:
				values = reflect.Append(values, reflect.ValueOf(v.(string)))
			case reflect.Int:
				intVal, _ := strconv.Atoi(v.(string))
				values = reflect.Append(values, reflect.ValueOf(intVal))
			default:
			}
		}
	}

	if err != nil {
		errPrintf(ActionHmgetKeysValues, err, "keys: %v, fields: %v", keys, fields)
	} else if values.IsValid() {
		errPrintf(ActionHmgetKeysValues, nil, "keys: %v, fields: %v, values: %v", keys, fields, values)
		reflect.ValueOf(in).Elem().Set(values)
	}
	return
}

// DeleteKeys 批量删除keys
func DeleteKeys(keys []string) (err error) {
	if len(keys) == 0 {
		return ErrKeyIsBlank
	}

	client := GetDefaultRedisClusterClient()
	for _, key := range keys {
		cmd := client.Del(key)
		if cmd == nil {
			err = ErrorUnknown
			errPrintf(ActionDeleteKeys, err, "keys: %v, key: %v", keys, key)
			return
		}
		if cmd.Err() != nil {
			err = cmd.Err()
			errPrintf(ActionDeleteKeys, err, "keys: %v, key: %v", keys, key)
			return
		}
	}

	errPrintf(ActionDeleteKeys, nil, "keys: %v", keys)
	return nil
}

// SetStringWithExpire 保存key的字串且有超时
func SetStringWithExpire(key string, value string, expire int) (err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.Set(key, value, time.Second*time.Duration(expire))
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionSetStringWithExpire, err, "key: %v, value: %v, expire: %v", key, value, expire)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionSetStringWithExpire, err, "key: %v, value: %v, expire: %v", key, value, expire)
		return
	}

	errPrintf(ActionSetStringWithExpire, nil, "key: %v, value: %v, expire: %v", key, len(value), expire)
	return nil
}

// HGetObject HGET指令获取数据，这里不做解析
func HGetObject(key, field string) (ret interface{}, err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.HGet(key, field)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionHGetObject, err, "key: %v, field: %v", key, field)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionHGetObject, err, "key: %v, field: %v", key, field)
		return
	}

	ret = []byte(cmd.String())
	errPrintf(ActionHGetObject, nil, "key: %v, field: %v, ret: %v", key, field, cmd.String())
	return
}

// HSetObject HSET指令设置数据
func HSetObject(key, field string, value interface{}) (err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.HSet(key, field, value)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionHSetObject, err, "key: %v, field: %v, value: %v", key, field, value)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionHSetObject, err, "key: %v, field: %v, value: %v", key, field, value)
		return
	}

	errPrintf(ActionHSetObject, nil, "key: %v, field: %v, value: %v", key, field, value)
	return nil
}

// HExists .
func HExists(key, field string) (ret bool, err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.HExists(key, field)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionHExists, err, "key: %v, field: %v", key, field)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionHExists, err, "key: %v, field: %v", key, field)
		return
	}

	ret = cmd.Val()
	errPrintf(ActionHExists, nil, "key: %v, field: %v, ret: %v", key, field, ret)
	return
}

// GetString 按key读取字串
func GetString(key string) (ret string, err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.Get(key)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionGetString, err, "key: %v", key)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		if err == redis.Nil {
			return "", nil
		}
		errPrintf(ActionGetString, err, "key: %v", key)
		return
	}

	ret = cmd.Val()
	errPrintf(ActionGetString, nil, "key: %v, ret: %v", key, len(ret))
	return
}

// SetObject 保存key的对象
func SetObject(key string, value interface{}) (err error) {
	var fieldsValues map[string]interface{}
	if fieldsValues, err = flattenStruct(value); err == nil {
		err = HMSET(key, fieldsValues)
	}
	errPrintf(ActionSetObject, err, "key: %v, value: %v", key, value)
	return err
}

// HmgetValues .
func HmgetValues(key string, fields []string, in interface{}) (err error) {
	return HmgetKeysValues([]string{key}, fields, in)
}

// SetNX setnx指令设置数据
func SetNX(key string, value interface{}, seconds int) (ret bool, err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.SetNX(key, value, time.Second*time.Duration(seconds))
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionSetNX, err, "key: %v, value: %v, seconds: %v", key, value, seconds)
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionSetNX, err, "key: %v, value: %v, seconds: %v", key, value, seconds)
		return
	}
	ret = cmd.Val()

	errPrintf(ActionSetNX, err, "key: %v, value: %v, seconds: %v, ret: %v", key, value, seconds, ret)
	return
}

// SetString 保存key的字串
func SetString(key string, value string) (err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.Set(key, value, 0)
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionSetString, err, "key: %v, Value: %v", key, value)
		return
	}

	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionSetString, err, "key: %v, Value: %+v", key, value)
		return
	}
	errPrintf(ActionSetString, err, "key: %v, Value: %+v", key, value)

	return nil
}

func Ping() (err error) {
	client := GetDefaultRedisClusterClient()
	cmd := client.Ping()
	if cmd == nil {
		err = ErrorUnknown
		errPrintf(ActionPing, err, "")
		return
	}
	if cmd.Err() != nil {
		err = cmd.Err()
		errPrintf(ActionPing, err, "")
		return
	}
	return nil
}
